﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Threading;
using Google.GData.Contacts;
using Google.GData.Client;
using Google.GData.Extensions;
using Google.Contacts;

namespace GoogleSync
{
    class GoogleContact : AContact
    {
        Contact entry;

        String first;
        String middle;
        String last;
        String title;

        ContactsRequest myService;

        bool newContact = false;

        private byte[] pictureBytes = null;

        static GoogleContact()
        {
            //Thread staticThread = new Thread(ThreadQueue);
            //staticThread.Start();
        }


        public GoogleContact(ContactsRequest myService,
                String googlegroup)
        {
            registerCalls();
            this.myService = myService;
            newContact = true;
            entry = new Contact();
            GroupMembership groupMembership = new GroupMembership();
            groupMembership.HRef = googlegroup;
            entry.GroupMembership.Add(groupMembership);
        }

        public override bool getDeleted()
        {
            return entry.Deleted;
        }

        public GoogleContact(Contact entry, ContactsRequest myService)
        {
            registerCalls();
            this.entry = entry;
            this.myService = myService;
            this.myService = myService;
        }

        private void loadPicture(String lastEtag)
        {
            if (cachedEtag.Equals(getPictureHash()))
            {
                return;
            }
            String filename = getGoogleContactID();
            //filename = filename.Substring(filename.LastIndexOf("/") + 1);
            //HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(entry.PhotoUri);
            //req.Headers.Add("Authorization", "GoogleLogin auth=" + myService.Service.QueryClientLoginToken());
            String saveEtag = entry.PhotoEtag;
            entry.PhotoEtag = "1"; // lastEtag;
            Stream pictureStream = myService.GetPhoto(entry);
            //if (req.ContentLength > 0)
            //{
            if (pictureStream != null)
            {
                //Stream pictureStream = req.GetResponse().GetResponseStream();
                MemoryStream outstream = new MemoryStream();
                byte[] buffer = new byte[4096];
                for (int read = 0; (read = pictureStream.Read(buffer, 0, 4096)) > 0;
                        outstream.Write(buffer, 0, read))
                {
                    Console.WriteLine("Read bytes: " + read);
                    Thread.Sleep(1000);
                }
                pictureBytes = outstream.ToArray();
                //FileStream fs = new FileStream(ContactSync.HOME + filename + ".jpg", FileMode.OpenOrCreate, FileAccess.Write);
                //fs.Write(pictureBytes, 0, pictureBytes.Length);
                //fs.Flush(); fs.Close();
            }
            entry.PhotoEtag = saveEtag;
        }

        private void registerCalls()
        {
            getFields.Add(Fields.FIRST, delegate
            {
                return getFirstName();
            });

            setFields.Add(Fields.FIRST, delegate(String value)
            {
                setFirstName(value);
            }
            );

            getFields.Add(Fields.MIDDLE, delegate
            {
                return getMiddleName();
            });

            setFields.Add(Fields.MIDDLE, delegate(String value)
            {
                setMiddleName(value);
            });

            getFields.Add(Fields.LAST, delegate
            {
                return getLastName();
            });

            setFields.Add(Fields.LAST, delegate(String value)
            {
                setLastName(value);
            });

            getFields.Add(Fields.TITLE, delegate
                {
                    return getTitleName();
                });

            setFields.Add(Fields.TITLE, delegate(String value)
            {
                setTitleName(value);
            });

            getFields.Add(Fields.NICKNAME, delegate
                {
                    return entry.ContactEntry.Nickname;
                });


            setFields.Add(Fields.NICKNAME, delegate(String value)
            {
                entry.ContactEntry.Nickname = value;
            });

            getFields.Add(Fields.COMPANY, delegate
                {
                    if (entry.Organizations.Count < 1) return "";
                    return entry.Organizations[0].Name;
                });

            setFields.Add(Fields.COMPANY, delegate(String value)
            {
                entry.Organizations.Clear();
                Organization neworg = new Organization();
                neworg.Name = value;
                neworg.Rel = ContactsRelationships.IsWork;
                if (value != null) entry.Organizations.Add(neworg);
            });

            getFields.Add(Fields.OTHER_PHONE, delegate
            {
                return getPhoneNumber(ContactsRelationships.IsOther, 1);
            });

            setFields.Add(Fields.OTHER_PHONE, delegate(String value)
            {
                setPhoneNumber(ContactsRelationships.IsOther, 1, value);
            });

            getFields.Add(Fields.BUSINESS_PHONE, delegate
            {
                return getPhoneNumber(ContactsRelationships.IsWork, 1);
            });

            setFields.Add(Fields.BUSINESS_PHONE, delegate(String value)
            {
                setPhoneNumber(ContactsRelationships.IsWork, 1, value);
            });

            getFields.Add(Fields.BUSINESS_PHONE2, delegate
            {
                return getPhoneNumber(ContactsRelationships.IsWork, 2);
            });

            setFields.Add(Fields.BUSINESS_PHONE2, delegate(String value)
            {
                setPhoneNumber(ContactsRelationships.IsWork, 2, value);
            });

            getFields.Add(Fields.HOME_PHONE, delegate
            {
                return getPhoneNumber(ContactsRelationships.IsHome, 1);
            });

            setFields.Add(Fields.HOME_PHONE, delegate(String value)
            {
                setPhoneNumber(ContactsRelationships.IsHome, 1, value);
            });

            getFields.Add(Fields.HOME_PHONE2, delegate
            {
                return getPhoneNumber(ContactsRelationships.IsHome, 2);
            });

            setFields.Add(Fields.HOME_PHONE2, delegate(String value)
            {
                setPhoneNumber(ContactsRelationships.IsHome, 2, value);
            });

            getFields.Add(Fields.MOBILE_PHONE, delegate
            {
                return getPhoneNumber(ContactsRelationships.IsMobile, 1);
            });

            setFields.Add(Fields.MOBILE_PHONE, delegate(String value)
            {
                setPhoneNumber(ContactsRelationships.IsMobile, 1, value);
            });

            getFields.Add(Fields.MOBILE_PHONE2, delegate
            {
                return getPhoneNumber(ContactsRelationships.IsMobile, 2);
            });

            setFields.Add(Fields.MOBILE_PHONE2, delegate(String value)
            {
                setPhoneNumber(ContactsRelationships.IsMobile, 2, value);
            });

            getFields.Add(Fields.EMAIL1, delegate
            {
                return getEmail(1);
            });

            setFields.Add(Fields.EMAIL1, delegate(String value)
            {
                setEmail(1, value);
            });

            getFields.Add(Fields.EMAIL2, delegate
            {
                return getEmail(2);
            });

            setFields.Add(Fields.EMAIL2, delegate(String value)
            {
                setEmail(2, value);
            });

            getFields.Add(Fields.EMAIL3, delegate
            {
                return getEmail(3);
            });

            setFields.Add(Fields.EMAIL3, delegate(String value)
            {
                setEmail(3, value);
            });

            getFields.Add(Fields.HOME_ADDR, delegate
            {
                return getPostalAddress(ContactsRelationships.IsHome, 1);
            });

            setFields.Add(Fields.HOME_ADDR, delegate(String value)
            {
                setPostalAddress(ContactsRelationships.IsHome, 1, value);
            });

            getFields.Add(Fields.BUSINESS_ADDR, delegate
            {
                return getPostalAddress(ContactsRelationships.IsWork, 1);
            });

            setFields.Add(Fields.BUSINESS_ADDR, delegate(String value)
            {
                setPostalAddress(ContactsRelationships.IsWork, 1, value);
            });

            getFields.Add(Fields.OUTID, delegate
            {
                foreach (ExtendedProperty prop in entry.ContactEntry.ExtendedProperties)
                {
                    if (prop.Name.Equals("Outlook"))
                    {
                        return prop.Value;
                    }
                }
                return "";
            });

            setFields.Add(Fields.OUTID, delegate(String value)
            {
                if (entry.ContactEntry.ExtendedProperties.Count < 1)
                {
                    ExtendedProperty id = new ExtendedProperty();
                    id.Name = "Outlook";
                    id.Value = value;
                    entry.ContactEntry.ExtendedProperties.Add(id);
                }
                else
                {
                    entry.ContactEntry.ExtendedProperties[0].Value = value;
                }
            });

        }

        private String getPhoneNumber(String rel, int count)
        {
            int c = 0;
            foreach (PhoneNumber number in entry.Phonenumbers)
            {
                if (number.Rel.Equals(rel))
                {
                    if (++c == count)
                    {
                        return number.Value;
                    }
                }
            }
            return "";
        }

        private void setPhoneNumber(String rel, int count, String value)
        {
            int c = 0;
            foreach (PhoneNumber number in entry.Phonenumbers)
            {
                if (number.Rel.Equals(rel))
                {
                    c++;
                    if (c == count)
                    {
                        if (value.Length < 1)
                        {
                            entry.Phonenumbers.Remove(number);
                            return;
                        }
                        else
                        {
                            number.Value = value;
                            return;
                        }
                    }
                }
            }
            if (value == null || value.Length < 1)
            {
                return;
            }
            PhoneNumber phoneNumber = new PhoneNumber();
            phoneNumber.Rel = rel;
            phoneNumber.Value = value;
            entry.Phonenumbers.Add(phoneNumber);

        }

        private String getPostalAddress(String rel, int count)
        {
            int c = 0;
            foreach (StructuredPostalAddress address in entry.PostalAddresses)
            {
                if (address.Rel.Equals(rel))
                {
                    if (++c == count)
                    {
                        String output = address.FormattedAddress;
                        while (output.EndsWith("\n"))
                        {
                            output = output.Substring(0, output.Length - 1);
                        }
                        return output;
                    }
                }
            }
            return "";
        }

        private void setPostalAddress(String rel, int count, String value)
        {
            int c = 0;
            foreach (StructuredPostalAddress postalAddress in entry.PostalAddresses)
            {
                if (postalAddress.Rel.Equals(rel))
                {
                    c++;
                    if (c == count)
                    {
                        if (value.Length < 1)
                        {
                            entry.PostalAddresses.Remove(postalAddress);
                            return;
                        }
                        else
                        {
                            postalAddress.FormattedAddress = value;
                            return;
                        }
                    }
                }
            }
            if (value == null || value.Length < 1)
            {
                return;
            }
            StructuredPostalAddress newPostalAddress = new StructuredPostalAddress();
            newPostalAddress.Rel = rel;
            newPostalAddress.FormattedAddress = value;
            entry.PostalAddresses.Add(newPostalAddress);

        }

        private String getEmail(int count)
        {
            int c = 0;
            foreach (EMail emailAddr in entry.Emails)
            {
                if (++c == count)
                {
                    return emailAddr.Address;
                }
            }
            return "";
        }

        private void setEmail(int count, String value)
        {
            int c = 0;
            foreach (EMail email in entry.Emails)
            {
                c++;
                if (c == count)
                {
                    if (value.Length < 1)
                    {
                        entry.Emails.Remove(email);
                        return;
                    }
                    else
                    {
                        email.Value = value;
                        return;
                    }
                }
            }
            if (value == null || value.Length < 1)
            {
                return;
            }
            EMail emailAddr = new EMail(value, ContactsRelationships.IsOther);
            entry.Emails.Add(emailAddr);

        }


        public String getFirstName()
        {
            fetchName();
            return first;
        }

        public void setFirstName(String name)
        {
            this.first = name;
            saveName();
        }

        public String getMiddleName()
        {
            fetchName();
            return middle;
        }

        public void setMiddleName(String name)
        {
            this.middle = name;
            saveName();
        }

        public Contact getUnderlyingEntry()
        {
            return entry;
        }

        public String getLastName()
        {
            fetchName();
            return last;
        }

        public String getTitleName()
        {
            fetchName();
            return title;
        }

        public void setLastName(String name)
        {
            this.last = name;
            saveName();
        }

        public void setTitleName(String name)
        {
            this.title = name;
            saveName();
        }

        private void saveName()
        {
            if (title == null || title.Length < 1)
                title = "";
            if (first == null || first.Length < 1)
                first = "";
            if (middle == null || middle.Length < 1)
                middle = "";
            if (last == null || last.Length < 1)
                last = "";
            String mid = (middle.Length < 1) ? "" : middle + " ";
            this.entry.Title = first + " " + mid + last;
            Name name = new Name();
            if (first != "")
                name.GivenName = first;
            if (middle != "")
                name.AdditonalName = middle;
            if (last != "")
                name.FamilyName = last;
            if (title != "")
                name.NamePrefix = title;
            this.entry.Name = name;
        }

        private void fetchName()
        {
            this.first = "";
            this.middle = "";
            this.last = "";
            this.title = "";
            String tit = entry.Title;
            if (tit != null)
            {
                int index = tit.LastIndexOf(' ');
                if (index < 0)
                {
                    first = tit;
                }
                else
                {
                    last = tit.Substring(index + 1);
                    first = tit.Substring(0, index);
                    index = first.IndexOf(' ');
                    if (index >= first.Length) index = -1;
                    if (index > 0)
                    {
                        middle = first.Substring(index + 1);
                        first = first.Substring(0, index);
                    }
                }
            }
        }

        public String getGoogleContactID()
        {
            return entry.Id;
        }

        public override void save()
        {
            if (changed)
            {
                if (newContact)
                {
                    lock (myService)
                    {
                        Feed<Contact> feed = myService.GetContacts();
                        entry = myService.Insert(feed, entry);
                    }
                }
                else
                {
                    lock (myService)
                    {
                        entry = myService.Update(entry);
                    }
                }
                changed = false;
            }

        }

        //private void updatePicture(ReferenceContact reference)
        //{
        //    if (pictureBytes != null && pictureBytes.Length > 0)
        //    {
        //        MemoryStream photoStream = new MemoryStream();
        //        photoStream.Write(pictureBytes, 0, pictureBytes.GetLength(0));
        //        photoStream.Seek(0, SeekOrigin.Begin);
        //        int tries = 0;
        //        addTask(delegate
        //        {
        //            Thread.Sleep(1000);
        //            Console.WriteLine("Uploading picture for: " + entry.Title);
        //            while (tries < 15)
        //            {
        //                try
        //                {
        //                    tries++;
        //                    Console.WriteLine(entry.PhotoUri);
        //                    lock (myService)
        //                    {
        //                        entry = myService.Update(entry);
        //                        myService.SetPhoto(entry, photoStream);
        //                        entry = myService.Update(entry);
        //                    }
        //                    reference.setGooglePictureHash(getPictureHash());
        //                    Console.WriteLine("Completed: " + entry.Title);
        //                    tries = 15;
        //                }
        //                catch (GDataRequestException ex)
        //                {
        //                    Console.WriteLine("Exception for: " + entry.Title);
        //                    Console.WriteLine(ex.GetBaseException().Message);
        //                    Console.WriteLine(ex.StackTrace);
        //                    System.Threading.Thread.Sleep(1000 * tries);
        //                }
        //            }
        //        });
        //    }

        //}

        public override void delete()
        {
            myService.Delete(entry);
        }


        private String cachedEtag = "";

        public override byte[] getPictureBytes(String lastEtag)
        {
            loadPicture(lastEtag);
            return pictureBytes;
        }


        public override String getPictureHash()
        {
            String tag = entry.PhotoEtag;
            return tag == null ? "" : tag;
        }


        public override void setPictureBytes(byte[] pictureBytes)
        {
            //this.changed = true;
            //this.pictureChanged = true;
            //this.pictureBytes = pictureBytes;
        }

        //public delegate void Task();

        //public static Queue<Task> taskQueue = new Queue<Task>();

        //public static void waitTillEmpty()
        //{
        //    lock (taskQueue)
        //    {
        //        while (taskQueue.Count > 0)
        //        {
        //        }
        //    }
        //}

        //public static void addTask(Task task)
        //{
        //    lock (taskQueue)
        //    {
        //        taskQueue.Enqueue(task);
        //        Monitor.PulseAll(taskQueue);
        //    }
        //}

        //public static void ThreadQueue()
        //{
        //    while (true)
        //    {
        //        Task task = null;
        //        lock (taskQueue)
        //        {
        //            while (task == null)
        //            {
        //                if (taskQueue.Count > 0)
        //                {
        //                    task = taskQueue.Dequeue();
        //                }
        //                else
        //                {
        //                    Monitor.Wait(taskQueue, 5000);
        //                }
        //            }
        //            task();
        //            Monitor.PulseAll(taskQueue);
        //        }
        //    }
        //}

    }
}
